#ifndef __INCLUDED_EVEREST_DBC_ODBC_ODBC_COMMON_H
#define __INCLUDED_EVEREST_DBC_ODBC_ODBC_COMMON_H

#include <everest/basedef.h>
#include <everest/dbc/dbc.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>
#include <stdint.h>


BEGIN_NAMESPACE_EVEREST

namespace dbc
{
namespace odbc
{
	struct OdbcColDesc  // ODBC column descriptor
	{
		SQLUSMALLINT colnum;
		SQLSMALLINT  namelen;
		SQLCHAR      colname[64];
		SQLCHAR     *pcolname;
		SQLSMALLINT  datatype;
		SQLULEN      colsize;
		SQLSMALLINT  decimaldigits;
		SQLSMALLINT  nullable;
		SQLSMALLINT  targettype;
		SQLULEN      targetsize;
		char    *    pValue;
		char    *    pIndOrLen;

		OdbcColDesc() {
			colnum = 0;
			colname[0] = '\0';
			pcolname = colname;
			colsize = 0;
			pValue = NULL;
			pIndOrLen = NULL;
		}

		OdbcColDesc(const OdbcColDesc& d)
		: colnum(d.colnum), namelen(d.namelen), datatype(d.datatype)
		, colsize(d.colsize), decimaldigits(d.decimaldigits), nullable(d.nullable)
		, targettype(d.targettype), targetsize(d.targetsize), pValue(d.pValue), pIndOrLen(d.pIndOrLen)
		{
			if ( d.pcolname == d.colname ) {
				pcolname = colname;
				strcpy((char *)pcolname, (const char *)d.pcolname);
			} else {
				pcolname = new SQLCHAR[namelen + 1];
				strcpy((char *)pcolname, (const char *)d.pcolname);
			}
		}

		~OdbcColDesc() {
			if ( pcolname != colname && pcolname != NULL )
				delete[] pcolname;
			pcolname = colname;
		}

		OdbcColDesc& operator=(const OdbcColDesc& d)
		{
			this->colnum = d.colnum;
			this->colsize = d.colsize;
			this->datatype = d.datatype;
			this->decimaldigits = d.decimaldigits;
			this->nullable = d.nullable;
			this->pValue = d.pValue;
			this->pIndOrLen = d.pIndOrLen;
			this->namelen = d.namelen;
			this->targetsize = d.targetsize;
			this->targettype = d.targettype;

			if ( d.pcolname == d.colname ) {
				pcolname = colname;
				strcpy((char *)pcolname, (const char *)d.pcolname);
			} else {
				pcolname = new SQLCHAR[namelen + 1];
				strcpy((char *)pcolname, (const char *)d.pcolname);
			}

			return *this;
		} // end of operator=

		static bool transToCType(OdbcColDesc *pDesc);

	}; // end of struct OdbcColDesc

	class OdbcException : public IException
	{
	public:
		OdbcException() throw();
		OdbcException(int code, const char *message, const char *source) throw();
		OdbcException(SQLSMALLINT handleType, SQLHANDLE handle, const char *pszSource) throw();
		OdbcException(const OdbcException& e) throw();
		~OdbcException() throw();

		OdbcException& operator=(const OdbcException& e) throw();

		void set(SQLSMALLINT handleType, SQLHANDLE handle, const char *pszSource) throw();

		virtual int    code() const;
		virtual String state() const;
		virtual String message() const;
		virtual String source() const;

	public:
		static OdbcException InvalidHandle(SQLSMALLINT handleType, SQLHANDLE handle, const char *pszSource);
		static OdbcException InvalidColumnIndex(int colIndex, int min, int max, const char *pszSource);
		static OdbcException InvalidTypeCast(int colIndex, SQLSMALLINT ctype, const char *nativeType, const char *pszSource);

	protected:
		SQLINTEGER m_Code;
		SQLCHAR    m_State[8];
		SQLCHAR    m_Source[64];
		SQLCHAR    m_Message[SQL_MAX_MESSAGE_LENGTH];
	};

	class OdbcEnvironment
	{
		EVEREST_UNCOPIABLE(OdbcEnvironment)
	public:
		OdbcEnvironment();
		virtual ~OdbcEnvironment();

		virtual IConnection * createConnection(const char *pszConStr);

	protected:
		SQLHANDLE m_EnvHandle;
	};

	class OdbcConnection : virtual public IConnection
	{
		EVEREST_UNCOPIABLE(OdbcConnection)
	public:
		OdbcConnection(SQLHANDLE hConn);
		virtual ~OdbcConnection() throw();

		virtual void release();
		virtual bool close();
		virtual void setAutoCommit(bool bAutoCommit);
		virtual bool getAutoCommit() const;
		virtual void commit();
		virtual void rollback();
		virtual bool isClosed() const;
		virtual IStatement * createStatement();
		virtual IPreparedStatement * prepareStatement(const char *pstrSql);

	protected:
		SQLHANDLE m_ConHandle;
	}; // end of class OTLConnection

	class OdbcStatement : virtual public IStatement
	{
		EVEREST_UNCOPIABLE(OdbcStatement)
	protected:
		static const int BATCH_SIZE = 64;

	public:
		OdbcStatement(SQLHANDLE hStm, OdbcConnection* pCon);
		virtual ~OdbcStatement();

		virtual void addBatch(const char *pszSql);
		virtual void cancel();
		virtual void clearBatch();
		virtual void close();
		virtual int  execute(const char *pszSql);
		virtual void executeBatch(NS_EVEREST::IntVector& results);
		virtual IResultSet * executeQuery(const char * pszSql);
		virtual int  executeUpdate(const char * pszSql);
		virtual IConnection * getConnection() const;
		virtual int  getQueryTimeout() const;
		virtual IResultSet * getResultSet() const;
		virtual int  getUpdateCount() const;
		virtual bool isClosed() const;
		virtual void release();
		virtual void setQueryTimeout(int seconds);
		virtual bool getCursorScrollable() const;
		virtual void setCursorScrollable(bool scrollable);
		virtual CursorType getCursorType() const;
		virtual void setCursorType(CursorType type);
		virtual void closeCursor();

	protected:
		SQLHANDLE       m_StmHandle;
		OdbcConnection *m_Conn;
		StringVector    m_SqlBatchList;
	}; // end of class OdbcStatement

	class OdbcParameter : virtual public IParameter
	{
		EVEREST_UNCOPIABLE(OdbcParameter)

	public:
		OdbcParameter(
				SQLHANDLE hStm,
				ValueType   vt,
				SQLSMALLINT inout,
				SQLULEN colsize,
				SQLSMALLINT decimalDigits);
		virtual ~OdbcParameter();

		virtual ValueType getParamType() const;
		virtual int       getIndex() const;
		virtual void      setIndex(int idx);
		virtual ParamInOutType getInOutType() const;

		virtual void  setInt(int value);
		virtual void  setInt64(int64_t value);
		virtual void  setFloat(float value) ;
		virtual void  setDouble(double value) ;
		virtual void  setString(const String& value);
		virtual void  setString(const char *pValue) ;
		virtual void  setBinary(const void *pValue, size_t len);
		virtual void  setBinary(const ByteVector& bytes);
		virtual void  setDateTime(const DateTime& dt);
		virtual void  setTimestamp(const Timestamp& ts);

		virtual bool  getInt(int& value) ;
		virtual bool  getInt64(int64_t& value) ;
		virtual bool  getFloat(float& value) ;
		virtual bool  getDouble(double& value) ;
		virtual bool  getString(String& value) ;
		virtual bool  getBinary(ByteVector& bytes);
		virtual bool  getDateTime(DateTime& dt) ;
		virtual bool  getTimestamp(Timestamp& ts) ;

	public:
		bool        bind() ;

	protected:
		SQLHANDLE   m_StmHandle;
		SQLINTEGER  m_Index;
		SQLSMALLINT m_DataType;     // SQL_C_<type>
		SQLSMALLINT m_ParamType;    // SQL_TYPE
		SQLSMALLINT m_InOut;
		SQLSMALLINT m_DecDigits;
		SQLULEN     m_ColSize;
		SQLLEN      m_LenOrInd;
		SQLLEN      m_buflen;
		char       *m_pBuffer; // pointer to real buffer
		bool        m_bBufferOwner;
	};

	class OdbcPreparedStatement : virtual public IPreparedStatement, public OdbcStatement
	{
		EVEREST_UNCOPIABLE(OdbcPreparedStatement)

		class ParameterHolder
		{
		public:
			ParameterHolder() : m_ptr(NULL) {}
			ParameterHolder(const ParameterHolder& param) : m_ptr(param.m_ptr) {}
			ParameterHolder& operator=(const ParameterHolder& param) { m_ptr = param.m_ptr; return *this;}
			operator OdbcParameter *() { return m_ptr;}
			operator const OdbcParameter *() { return m_ptr;}
			ParameterHolder& operator=(OdbcParameter * param) { m_ptr = param; return *this;}

		private:
			OdbcParameter *	m_ptr;
		};

		typedef std::vector<ParameterHolder>  ParamVector;

	public:
		OdbcPreparedStatement(SQLHANDLE hStm, OdbcConnection* pCon);
		virtual ~OdbcPreparedStatement();

		virtual void clearParameters();
		virtual void execute();
		virtual IResultSet * executeQuery();
		virtual int  executeUpdate();

		virtual IParameter * createParameter(ValueType vt, ParamInOutType inout, size_t colsize, int decimalDigits);
		virtual bool addParameter(IParameter *pParam) ;

	protected:
		int          m_paramCount;
		ParamVector  m_paramVector;
	}; // end of class OdbcPreparedStatement

	class OdbcResultSet : virtual public IResultSet
	{
		EVEREST_UNCOPIABLE(OdbcResultSet)

		typedef std::vector<OdbcColDesc> ColumnDescVector;

	public:
		OdbcResultSet(SQLHANDLE hStm, OdbcConnection* pCon, SQLSMALLINT cols);
		OdbcResultSet(SQLHANDLE hStm, OdbcConnection* pCon, SQLSMALLINT cols, SQLRETURN result);
		virtual ~OdbcResultSet();

		virtual void release();
		virtual void close();
		virtual bool first();
		virtual bool last();
		virtual bool next();
		virtual bool previous();
		virtual bool absolute(ssize_t row);
		virtual bool relative(ssize_t rows);

		virtual bool  getByte(int col, char& val) const ;
		virtual bool  getShort(int col, short& val) const ;
		virtual bool  getInt(int col, int& val) const ;
		virtual bool  getInt64(int col, int64_t& val) const;
		virtual bool  getFloat(int col, float& val) const;
		virtual bool  getDouble(int col, double& val) const;
		virtual bool  getString(int col, String& str) const;
		virtual bool  getBinary(int col, ByteVector& bytes) const ;
		virtual bool  getDateTime(int col, DateTime& dt) const;
		virtual bool  getTimestamp(int col, Timestamp& timestamp) const;

	protected:
		SQLRETURN bindColumns() throw();
		SQLRETURN unbindColumns() throw();

	protected:
		OdbcConnection  *m_pCon;
		SQLHANDLE        m_StmHandle;
		SQLSMALLINT      m_Columns;
		int              m_PrevReturn;
		ColumnDescVector m_ColDescVec;
		char            *m_RowBuffer;
	}; // end of class OTLResultSet

}} // end of namespace dbc::odbc

END_NAMESPACE_EVEREST

// #include <everest/dbc/odbc/odbc_type_converter.h>

#endif // __INCLUDED_EVEREST_DBC_ODBC_ODBC_COMMON_H
